package com.bear.atlassianmcp.oauth.service;

import com.bear.atlassianmcp.oauth.config.OAuthProperties;
import com.bear.atlassianmcp.oauth.model.OAuthToken;
import com.bear.atlassianmcp.oauth.model.OAuthToken.ServiceType;
import com.bear.atlassianmcp.oauth.model.OAuthToken.TokenType;
import com.bear.atlassianmcp.oauth.util.OAuthSignatureUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.awt.Desktop;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * OAuth 1.0a 核心服务
 * 实现完整的 OAuth 1.0a 三步授权流程
 */
@Service
public class OAuthService {
    
    private static final Logger logger = LoggerFactory.getLogger(OAuthService.class);
    
    private final OAuthProperties oauthProperties;
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    private final OAuthSignatureUtil signatureUtil;
    
    // 存储待授权的 Request Token
    private final Map<String, OAuthToken> pendingTokens = new ConcurrentHashMap<>();
    
    // 存储授权完成的信号量
    private final Map<String, CountDownLatch> authorizationLatches = new ConcurrentHashMap<>();
    
    @Autowired
    public OAuthService(OAuthProperties oauthProperties, RestTemplate oauthRestTemplate,
                       ObjectMapper objectMapper, OAuthSignatureUtil signatureUtil) {
        this.oauthProperties = oauthProperties;
        this.restTemplate = oauthRestTemplate;
        this.objectMapper = objectMapper;
        this.signatureUtil = signatureUtil;
        
        // 确保 token 存储目录存在
        createTokenStorageDirectory();
    }

    /**
     * 开始 OAuth 授权流程
     *
     * @param serviceType 服务类型 (JIRA 或 CONFLUENCE)
     * @return 授权URL
     */
    public String getAuthUrl(ServiceType serviceType) {
        logger.info("Starting OAuth authorization flow for {}", serviceType);

        try {
            // 第一步：获取 Request Token
            OAuthToken requestToken = getRequestToken(serviceType);
            logger.info("Obtained request token: {}", requestToken);

            // 第二步：用户授权
            String authorizationUrl = buildAuthorizationUrl(requestToken, serviceType);
            logger.info("Authorization URL: {}", authorizationUrl);

            String tokenKey = requestToken.getToken();
            pendingTokens.put(tokenKey, requestToken);

            return authorizationUrl;

        } catch (Exception e) {
            logger.error("OAuth authorization flow failed for {}: {}", serviceType, e.getMessage(), e);
            throw new RuntimeException("OAuth authorization failed: " + e.getMessage(), e);
        }
    }
    
    /**
     * 开始 OAuth 授权流程
     * 
     * @param serviceType 服务类型 (JIRA 或 CONFLUENCE)
     * @return 授权后的 Access Token
     */
    public OAuthToken startAuthorizationFlow(ServiceType serviceType) {
        logger.info("Starting OAuth authorization flow for {}", serviceType);
        
        try {
            // 第一步：获取 Request Token
            OAuthToken requestToken = getRequestToken(serviceType);
            logger.info("Obtained request token: {}", requestToken);
            
            // 第二步：用户授权
            String authorizationUrl = buildAuthorizationUrl(requestToken, serviceType);
            logger.info("Authorization URL: {}", authorizationUrl);
            
            // 打开浏览器进行授权
            if (oauthProperties.getCommon().isAutoOpenBrowser()) {
                openBrowser(authorizationUrl);
            } else {
                logger.info("Please open the following URL in your browser to authorize the application:");
                logger.info(authorizationUrl);
            }
            
            // 等待用户授权完成
            waitForAuthorization(requestToken);
            
            // 第三步：获取 Access Token
            OAuthToken accessToken = getAccessToken(requestToken);
            logger.info("Successfully obtained access token: {}", accessToken);
            
            // 保存 Access Token
            saveToken(accessToken);
            
            return accessToken;
            
        } catch (Exception e) {
            logger.error("OAuth authorization flow failed for {}: {}", serviceType, e.getMessage(), e);
            throw new RuntimeException("OAuth authorization failed: " + e.getMessage(), e);
        }
    }
    
    /**
     * 获取 Request Token
     */
    public OAuthToken getRequestToken(ServiceType serviceType) {
        logger.debug("Getting request token for {}", serviceType);
        
        String requestTokenUrl = getRequestTokenUrl(serviceType);
        String callbackUrl = getCallbackUrl(serviceType);
        
        // 构建 OAuth 参数
        Map<String, String> oauthParams = new HashMap<>();
        oauthParams.put("oauth_consumer_key", getConsumerKey(serviceType));
        oauthParams.put("oauth_signature_method", "RSA-SHA1");
        oauthParams.put("oauth_timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        oauthParams.put("oauth_nonce", UUID.randomUUID().toString());
        oauthParams.put("oauth_version", "1.0");
        oauthParams.put("oauth_callback", callbackUrl);
        
        // 生成签名
        String signature = signatureUtil.generateSignature("POST", requestTokenUrl, oauthParams, 
                                                          "", getPrivateKey(serviceType));
        oauthParams.put("oauth_signature", signature);
        
        // 构建 Authorization 头
        String authHeader = signatureUtil.buildAuthorizationHeader(oauthParams);
        
        // 发送请求
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", authHeader);
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        
        HttpEntity<String> entity = new HttpEntity<>(headers);
        
        try {
            ResponseEntity<String> response = restTemplate.exchange(
                requestTokenUrl, HttpMethod.POST, entity, String.class);
            
            if (response.getStatusCode() == HttpStatus.OK) {
                return parseTokenResponse(response.getBody(), TokenType.REQUEST_TOKEN, serviceType);
            } else {
                throw new RuntimeException("Failed to get request token: " + response.getStatusCode());
            }
            
        } catch (Exception e) {
            logger.error("Error getting request token: {}", e.getMessage());
            throw new RuntimeException("Failed to get request token", e);
        }
    }
    
    /**
     * 构建授权 URL
     */
    public String buildAuthorizationUrl(OAuthToken requestToken, ServiceType serviceType) {
        String authorizationUrl = getAuthorizationUrl(serviceType);
        
        Map<String, String> params = new HashMap<>();
        params.put("oauth_token", requestToken.getToken());
        String queryParam = params.entrySet().stream()
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining("&"));
        return authorizationUrl + "?" + queryParam;
    }
    
    /**
     * 等待用户授权完成
     */
    private void waitForAuthorization(OAuthToken requestToken) {
        String tokenKey = requestToken.getToken();
        pendingTokens.put(tokenKey, requestToken);
        
        CountDownLatch latch = new CountDownLatch(1);
        authorizationLatches.put(tokenKey, latch);
        
        try {
            boolean authorized = latch.await(
                oauthProperties.getCommon().getAuthorizationTimeout().toMillis(), 
                TimeUnit.MILLISECONDS);
            
            if (!authorized) {
                throw new RuntimeException("Authorization timeout. Please complete authorization within " + 
                                         oauthProperties.getCommon().getAuthorizationTimeout().toMinutes() + " minutes.");
            }
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Authorization was interrupted", e);
        } finally {
            // 清理
            pendingTokens.remove(tokenKey);
            authorizationLatches.remove(tokenKey);
        }
    }
    
    /**
     * 获取 Access Token
     */
    private OAuthToken getAccessToken(OAuthToken requestToken) {
        logger.debug("Getting access token for request token: {}", requestToken.getToken());
        
        ServiceType serviceType = requestToken.getServiceType();
        String accessTokenUrl = getAccessTokenUrl(serviceType);
        
        // 构建 OAuth 参数
        Map<String, String> oauthParams = new HashMap<>();
        oauthParams.put("oauth_consumer_key", getConsumerKey(serviceType));
        oauthParams.put("oauth_token", requestToken.getToken());
        oauthParams.put("oauth_signature_method", "RSA-SHA1");
        oauthParams.put("oauth_timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        oauthParams.put("oauth_nonce", UUID.randomUUID().toString());
        oauthParams.put("oauth_version", "1.0");
        oauthParams.put("oauth_verifier", requestToken.getVerifier());
        
        // 生成签名
        String signature = signatureUtil.generateSignature("POST", accessTokenUrl, oauthParams, 
                                                          requestToken.getTokenSecret(), getPrivateKey(serviceType));
        oauthParams.put("oauth_signature", signature);
        
        // 构建 Authorization 头
        String authHeader = signatureUtil.buildAuthorizationHeader(oauthParams);
        
        // 发送请求
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", authHeader);
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        
        HttpEntity<String> entity = new HttpEntity<>(headers);
        
        try {
            ResponseEntity<String> response = restTemplate.exchange(
                accessTokenUrl, HttpMethod.POST, entity, String.class);
            
            if (response.getStatusCode() == HttpStatus.OK) {
                OAuthToken accessToken = parseTokenResponse(response.getBody(), TokenType.ACCESS_TOKEN, serviceType);
                accessToken.setAuthorized(true);
                return accessToken;
            } else {
                throw new RuntimeException("Failed to get access token: " + response.getStatusCode());
            }
            
        } catch (Exception e) {
            logger.error("Error getting access token: {}", e.getMessage());
            throw new RuntimeException("Failed to get access token", e);
        }
    }
    
    /**
     * 解析 Token 响应
     */
    private OAuthToken parseTokenResponse(String responseBody, TokenType tokenType, ServiceType serviceType) {
        logger.debug("Parsing token response: {}", responseBody);
        
        Map<String, String> params = new HashMap<>();
        String[] pairs = responseBody.split("&");
        
        for (String pair : pairs) {
            String[] keyValue = pair.split("=");
            if (keyValue.length == 2) {
                params.put(keyValue[0], keyValue[1]);
            }
        }
        
        String token = params.get("oauth_token");
        String tokenSecret = params.get("oauth_token_secret");
        
        if (token == null || tokenSecret == null) {
            throw new RuntimeException("Invalid token response: missing token or token_secret");
        }
        
        OAuthToken oauthToken = new OAuthToken(token, tokenSecret, tokenType, serviceType);
        
        // 如果是 Request Token，可能包含其他信息
        if (tokenType == TokenType.REQUEST_TOKEN) {
            String callbackConfirmed = params.get("oauth_callback_confirmed");
            if (!"true".equals(callbackConfirmed)) {
                logger.warn("OAuth callback not confirmed: {}", callbackConfirmed);
            }
        }
        
        return oauthToken;
    }
    
    /**
     * 处理授权回调
     *
     * @param token OAuth 请求令牌
     * @param verifier OAuth 验证码
     * @return 获得的访问令牌
     */
    public OAuthToken handleAuthorizationCallback(String token, String verifier) {
        logger.info("Handling authorization callback for token: {}", token);
        
        OAuthToken requestToken = pendingTokens.get(token);
        if (requestToken == null) {
            logger.error("No pending request token found for: {}", token);
            throw new RuntimeException("Invalid or expired request token");
        }
        
        try {
            // 设置验证码
            requestToken.setVerifier(verifier);
            requestToken.setAuthorized(true);

            // 获取访问令牌
            OAuthToken accessToken = getAccessToken(requestToken);

            // 保存访问令牌
            // saveToken(accessToken);

            // 清理待处理的令牌
            pendingTokens.remove(token);

            // 通知等待的线程
            CountDownLatch latch = authorizationLatches.get(token);
            if (latch != null) {
                latch.countDown();
                authorizationLatches.remove(token);
            }

            logger.info("Authorization callback processed successfully, access token obtained for: {}",
                       accessToken.getServiceType());

            return accessToken;

        } catch (Exception e) {
            logger.error("Failed to process authorization callback: {}", e.getMessage(), e);

            // 清理资源
            pendingTokens.remove(token);
            CountDownLatch latch = authorizationLatches.get(token);
            if (latch != null) {
                latch.countDown();
                authorizationLatches.remove(token);
            }

            throw new RuntimeException("Failed to obtain access token: " + e.getMessage(), e);
        }
    }
    
    // 辅助方法
    private String getRequestTokenUrl(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getRequestTokenUrl() : 
               oauthProperties.getConfluence().getRequestTokenUrl();
    }
    
    private String getAuthorizationUrl(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getAuthorizationUrl() : 
               oauthProperties.getConfluence().getAuthorizationUrl();
    }
    
    private String getAccessTokenUrl(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getAccessTokenUrl() : 
               oauthProperties.getConfluence().getAccessTokenUrl();
    }
    
    private String getCallbackUrl(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getCallbackUrl() : 
               oauthProperties.getConfluence().getCallbackUrl();
    }
    
    private String getConsumerKey(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getConsumerKey() : 
               oauthProperties.getConfluence().getConsumerKey();
    }
    
    private String getPrivateKey(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getPrivateKey() : 
               oauthProperties.getConfluence().getPrivateKey();
    }
    
    /**
     * 打开浏览器
     */
    private void openBrowser(String url) {
        try {
            if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
                Desktop.getDesktop().browse(URI.create(url));
                logger.info("Opened browser for authorization");
            } else {
                logger.warn("Desktop browsing not supported. Please manually open: {}", url);
            }
        } catch (IOException e) {
            logger.error("Failed to open browser: {}", e.getMessage());
            logger.info("Please manually open the following URL: {}", url);
        }
    }
    
    /**
     * 创建 Token 存储目录
     */
    private void createTokenStorageDirectory() {
        try {
            Path tokenDir = Paths.get(oauthProperties.getCommon().getTokenStorePath());
            if (!Files.exists(tokenDir)) {
                Files.createDirectories(tokenDir);
                logger.info("Created token storage directory: {}", tokenDir);
            }
        } catch (IOException e) {
            logger.error("Failed to create token storage directory: {}", e.getMessage());
        }
    }
    
    /**
     * 保存 Token 到文件
     */
    private void saveToken(OAuthToken token) {
        try {
            String fileName = token.getServiceType().name().toLowerCase() + "_access_token.json";
            Path tokenFile = Paths.get(oauthProperties.getCommon().getTokenStorePath(), fileName);
            
            objectMapper.writeValue(tokenFile.toFile(), token);
            logger.info("Saved access token to: {}", tokenFile);
            
        } catch (IOException e) {
            logger.error("Failed to save token: {}", e.getMessage());
        }
    }
    
    /**
     * 从文件加载 Token
     */
    public OAuthToken loadToken(ServiceType serviceType) {
        try {
            String fileName = serviceType.name().toLowerCase() + "_access_token.json";
            Path tokenFile = Paths.get(oauthProperties.getCommon().getTokenStorePath(), fileName);
            
            if (Files.exists(tokenFile)) {
                OAuthToken token = objectMapper.readValue(tokenFile.toFile(), OAuthToken.class);
                if (token.isValidAccessToken()) {
                    logger.debug("Loaded valid access token for {}", serviceType);
                    return token;
                } else {
                    logger.warn("Loaded token for {} is invalid or expired", serviceType);
                }
            }
            
        } catch (IOException e) {
            logger.error("Failed to load token for {}: {}", serviceType, e.getMessage());
        }
        
        return null;
    }
    
    /**
     * 检查是否已有有效的 Access Token
     */
    public boolean hasValidToken(ServiceType serviceType) {
        OAuthToken token = loadToken(serviceType);
        return token != null && token.isValidAccessToken();
    }
}
